export type Constructor<T = any> = new (...args: any[]) => T;

export type DecoratorTarget<C extends {} = any> = { constructor: C, }

export class Injection<T extends Function> {
	private injectableMap = new Map<string, Constructor<T>>();

	private injectSet = new Set<{ target: Constructor<T>, targetKey: string, name: string }>();

	private ruleMap = new Map<string, T>();

	public injectable() {
		return (_target: Function): void => {
			const target = _target as Constructor<T>;
			this.injectableMap.set(target.name, target);
		};
	};

	public inject(name?: string) {
		return <K extends string>(_target: DecoratorTarget<Function>, targetKey: K, index?: undefined): void => {
			const target = _target.constructor as Constructor<T>;
			this.injectSet.add({ target: target, targetKey, name: name || targetKey });
		};
	};

	public building(): void {
		for (const [name, target] of Array.from(this.injectableMap)) {
			this.ruleMap.set(name, new target);
		}
		for (const { target, targetKey, name } of Array.from(this.injectSet)) {
			const targetName = target.name;
			const instance = this.ruleMap.get(targetName);
			if (instance === undefined) {
				throw new Error("注入失败，未找到被注入项，名称为：" + targetName);
			}
			const info = this.ruleMap.get(name);
			if (info === undefined) {
				throw new Error("注入失败，未找到注入元素，名称为：" + name);
			}
			(instance as any)[targetKey] = info;
		}
	};

	public getElement(name: string) {
		const rule = this.ruleMap.get(name);
		if (rule === undefined) {
			throw new Error("未找到规则，名称为：" + name);
		}
		return rule;
	};

	public getElementAll() {
		return this.ruleMap.values();
	};

}
